<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>My first three.js app</title>
  <style>
    body {
      margin: 0;
    }
  </style>
</head>

<body>
  <script src="js/three.min.js"></script>
  <script src="js/Pass.js"></script>
  <script src="js/ShaderPass.js"></script>
  <script src="js/EffectComposer.js"></script>
  <script src="js/LuminosityHighPassShader.js"></script>
  <script src="js/CopyShader.js"></script>
  <script src="js/RenderPass.js"></script>
  <script src="js/UnrealBloomPass.js"></script>
  <script>
    var scene, camera, renderer, bloomComposer, points, edgeDiffusionPoints, centerDiffusionPoints;
    var width = window.innerWidth,
      height = window.innerHeight;
    const ENTIRE_SCENE = 0,
      BLOOM_SCENE = 1;
    const bloomLayer = new THREE.Layers();
    bloomLayer.set(BLOOM_SCENE);

    function heartFunction(number, ratio = 10) {
      const points = [];

      for (let i = 0; i < number; i++) {
        const t = Math.random() * 2 * Math.PI
        // const sphere = new THREE.Mesh(geometry, material);

        x = 16 * Math.pow(Math.sin(t), 3);
        y = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
        z = 0;

        x *= ratio;
        y *= ratio;
        z *= ratio;


        points.push(new THREE.Vector3(x, y, z));
      }
      return points;
    }

    function scatterInside(point, beta = 0.15) {
      ratio_x = -beta * Math.log(Math.random())
      ratio_y = -beta * Math.log(Math.random())

      dx = ratio_x * point.x
      dy = ratio_y * point.y
      dz = 0

      return new THREE.Vector3(point.x - dx, point.y - dy, point.z - dz);
    }

    function shrink(point, ratio) {
      force = -1 / Math.pow(x * x + y * y, 0.6);
      dx = ratio * force * point.x;
      dy = ratio * force * point.y;
      dz = 0
      return new THREE.Vector3(point.x - dx, point.y - dy, point.z - dz);
    }

    function curve(p) {
      return 2 * (3 * Math.sin(4 * p)) / (2 * Math.PI)
    }


    function build(number) {
      const ps = heartFunction(number, 25);
      const edps = [];
      const cdps = [];
      for (let i = 0; i < ps.length; i++) {
        for (let j = 0; j < 5; j++) {
          edps.push(scatterInside(ps[i], 0.08));
        }
      }

      for (let i = 0; i < 4000; i++) {
        cdps.push(scatterInside(ps[Math.floor(Math.random() * ps.length)], 0.17))
      }

      const heartMaterial = new THREE.PointsMaterial({
        size: 2,
        color: 0xe77c8e
      });
      points = new THREE.Points(new THREE.BufferGeometry().setFromPoints(ps), heartMaterial);
      scene.add(points);
      edgeDiffusionPoints = new THREE.Points(new THREE.BufferGeometry().setFromPoints(edps), heartMaterial);
      scene.add(edgeDiffusionPoints);
      centerDiffusionPoints = new THREE.Points(new THREE.BufferGeometry().setFromPoints(cdps), heartMaterial);
      scene.add(centerDiffusionPoints);

      points.userData = [...points.geometry.attributes.position.array];
      edgeDiffusionPoints.userData = [...edgeDiffusionPoints.geometry.attributes.position.array];
      centerDiffusionPoints.userData = [...centerDiffusionPoints.geometry.attributes.position.array];
    }

    function onDocumentReady() {
      initialize()
    }

    function initialize() {
      initScene();
      initCamera();
      initRenderer();

      setComposer();

      build(2000);

      animate();
    }


    function initScene() {
      scene = new THREE.Scene();
    }

    function initCamera() {
      camera = new THREE.OrthographicCamera(width / -2, width / 2, height / -2, height / 2, 0.1, 1000);
      camera.position.set(0, 0, 100);
    }

    function initRenderer() {
      renderer = new THREE.WebGLRenderer();
      renderer.setSize(width, height);
      document.body.appendChild(renderer.domElement);
    }

    function setComposer() {
      const renderPass = new THREE.RenderPass(scene, camera);
      const bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4,
        0.85);
      bloomPass.threshold = 0.2;
      bloomPass.strength = 0.9; // 光辉强度
      bloomPass.radius = 0;

      bloomComposer = new THREE.EffectComposer(renderer);
      bloomComposer.addPass(renderPass);
      bloomComposer.addPass(bloomPass);
    }


    function animate() {
      requestAnimationFrame(animate)
      bloomComposer.render();
      // renderer.render(scene, camera);
      render()
    }
    let t0 = new Date(),
      dt = 0;

    function render() {
      const now = new Date();
      let negative = 1;

      dt = now - t0 - dt;
      if (now - t0 >= 1000) {
        negative = -1
        t0 = now;
      }
      const frame = (dt % 60) / 30;

      let ratio = 10 + 10 * curve(now);



      calPosition(points, points.geometry.attributes.position.array, ratio)
      calPosition(edgeDiffusionPoints, edgeDiffusionPoints.geometry.attributes.position.array, ratio)
      calPosition(edgeDiffusionPoints, centerDiffusionPoints.geometry.attributes.position.array, ratio)
      points.geometry.attributes.position.needsUpdate = true; // 需要加在第一次渲染之后
      edgeDiffusionPoints.geometry.attributes.position.needsUpdate = true; // 需要加在第一次渲染之后
      centerDiffusionPoints.geometry.attributes.position.needsUpdate = true; // 需要加在第一次渲染之后
    }

    function calPosition(points, positions, ratio) {

      for (let i = 0; i < positions.length; i = i + 3) {
        let ox = points.userData[i];
        let oy = points.userData[i + 1];
        let oz = points.userData[i + 2];

        let x = positions[i];
        let y = positions[i + 1];
        let z = positions[i + 2];

        let force = 1 / Math.pow(x * x + y * y, 0.6);
        let dx = ratio * force * ox + (-Math.random() * 2 + 1);
        let dy = ratio * force * oy + (-Math.random() * 2 + 1);

        positions[i] = ox + dx;
        positions[i + 1] = oy + dy;
        positions[i + 2] = z;

      }

    }

    document.addEventListener('DOMContentLoaded', onDocumentReady);
  </script>
</body>

</html>